/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.p2psimulator.entity.overlaynetwork.routingtable;

import java.util.List;
import org.p2psimulator.entity.Id;
import org.p2psimulator.entity.id.DoubleId;
import org.p2psimulator.entity.overlaynetwork.structures.Ring;

/**
 *
 * @author Amorim
 */
public abstract class ChordRoutingTable<V> extends RoutingTable {

    protected int M = 8;
    protected Id owner;
    protected Id sucessor;
    protected Id predecessor;
    protected Ring<V> fingers;
    protected boolean sucessorOnline = true;

    public ChordRoutingTable(int M, Ring<V> ring, Id owner) {

        if (owner==null){
            throw new IllegalArgumentException("Table owner can not be null!");
        }
        this.fingers = ring;
        this.M = M;
        this.owner = owner;
        this.sucessor = owner;
        this.predecessor = owner;
    }

    public Id getClosestPreceding(Id id) {

        Double idValue = (Double) id.getValue();
        Double sucValue = (Double) sucessor.getValue();
        Id closest = new DoubleId((Double.NaN));
        Double max = -Double.MAX_VALUE;

        if (sucValue <= idValue && sucValue > max) {
            max = sucValue;
            closest = sucessor;
        }
        for (Id finger : fingers.getNodes()) {

            Double fingerValue = (Double) finger.getValue();

            if (fingerValue < idValue && fingerValue > max) {
                max = fingerValue;
                closest = finger;
            }
        }
        /**
         * Caso não haja nenhum menor do que id, é pq este é
         * menor do que todos na fingertable. Logo,
         * seu sucessor mais provável é o sucessor do último
         * elemento na tabela
         */
        if (closest == null && fingers.size() > 0) {
            closest = fingers.getNodes().lastElement();
        }
        return closest;

    }

    public List<Id> getAllPrecedingNodes(Id key){
        return fingers.getAllPredecessors(key);
    }
    public boolean isMySucessor(Id key) {
        return fingers.hasSucessor(key);
    }

    @Override
    public void clear() {
        fingers.clear();
    }

    public boolean isSucessorOnline() {
        return sucessorOnline;
    }

    public void setSucessorOnline(boolean sucessorOnline) {
        this.sucessorOnline = sucessorOnline;
    }

    @Override
    public synchronized boolean contains(Id peer) {
        if (peer==null){
            throw  new IllegalArgumentException("Trying to compare with nullS");
        }
        if (owner == null){
            throw  new IllegalStateException("Chord Routing table without a owner");
        }
        if (sucessor == null){
            throw  new IllegalStateException("Chord Routing table without a sucessor");
        }
        if (predecessor == null){
            throw  new IllegalStateException("Chord Routing table without a predecessor");
        }
        if (peer.getValue() == owner.getValue()
                || peer.getValue() == sucessor.getValue()
                || peer.getValue() == predecessor.getValue()) {
            return true;
        } else {
            return fingers.contains(peer);
        }
    }

    @Override
    public boolean deletePeer(Id peer) {
        if (fingers.contains(peer)) {
            return fingers.remove(peer);
        } else {
            return false;
        }
    }

    @Deprecated
    public void insertNeighbor(Id peer) {
        fingers.insertNode(peer, null);
    }

    public abstract void insertNode(Id peer, V value);

    public int size() {
        return fingers.size();
    }

    public V getEntry(int idx) {
        if (fingers.size() > 0) {
            return fingers.get(idx);
        } else {
            return null;
        }
    }

    public Id getOwner() {
        return owner;
    }

    public void setOwner(Id owner) {
        this.owner = owner;
    }

    public Id getPredecessor() {
        return predecessor;
    }

    public void setPredecessor(Id predecessor) {
        if (sucessor==null){
            throw new IllegalArgumentException("Predecessor must be different of NULL");
        }
        this.predecessor = predecessor;
    }

    public Id getSucessor() {
        return sucessor;
    }

    public void setSucessor(Id sucessor) {
        if (sucessor==null){
            throw new IllegalArgumentException("Sucessor must be different of NULL");
        }
        this.sucessor = sucessor;
    }
}
